package com.yum.Annotation.Processor;

import com.sun.source.tree.Tree;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;
import com.yum.Annotation.YGet;


@SupportedAnnotationTypes("com.yum.Annotation.YGet")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class YGetterProcessor extends AbstractProcessor {
    /**
     * 输出日志
     */
    private Messager messager;

    /**
     * 将Element转为JCTree工具,提供了待处理的抽象语法树
     */
    private JavacTrees javacTrees;

    /**
     * 封装了创建AST节点的一些方法
     */
    private TreeMaker treeMaker;

    /**
     * 提供了创建标识符的方法
     */
    private Names names;

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment)processingEnv).getContext();
        this.messager = processingEnv.getMessager();
        this.javacTrees = JavacTrees.instance(processingEnv);
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }


    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 获取被@Getter注解标记的所有元素(这个元素可能是类、变量、方法等等)
       Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(YGet.class);

       for(Element element:set){
           JCTree jcTree = javacTrees.getTree(element);
           jcTree.accept(new TreeTranslator(){
               @Override
               public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                   //创建一个变量语法树节点的list
                   List<JCTree.JCVariableDecl> jcVariableDeclList = com.sun.tools.javac.util.List.nil();
                   // 遍历defs，即是类定义的详细语句，包括字段、方法的定义等等
                   for(JCTree tree:jcClassDecl.defs){
                       if(tree.getKind().equals(Tree.Kind.VARIABLE)){
                           JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                           jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                       }
                   }
                   //对变量进行生成操作
                   for(JCTree.JCVariableDecl jcVariableDecl:jcVariableDeclList){
                       messager.printMessage(Diagnostic.Kind.NOTE,"get"
                               + jcVariableDecl.getName() + "has been processed");
                       treeMaker.pos = jcVariableDecl.pos;
                       jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                   }
                   super.visitClassDef(jcClassDecl);
               }
           });
       }
       return true;
    }

    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl){

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();

        statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")),
                jcVariableDecl.getName())));

        JCTree.JCBlock body = treeMaker.Block(0,statements.toList());

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),//访问修饰符
                getNewMethodName(jcVariableDecl.getName()),//方法名
                jcVariableDecl.vartype,//返回类型
                List.nil(),//泛型参数列表
                List.nil(),//参数列表
                List.nil(),//异常声明列表
                body,//方法体
                null
        );
    }

    private com.sun.tools.javac.util.Name getNewMethodName(Name name){
        String s = name.toString();
        return names.fromString("get"+s.substring(0,1).toUpperCase()
        + s.substring(1,name.length()));
    }


}
